#pragma once

#include "fxcc/mono/pch.h"
#include "fxcc/mono/Method.h"
#include "fxcc/mono/Field.h"
#include "fxcc/mono/Args.h"

namespace mono
{
    struct FXCC_API Object
    {
        MonoObject *object;

        MonoClass *klass;

        std::map<std::string ,std::shared_ptr<mono::Field>> m_Fields;

        Object() : object(0), klass(0) {}

        Object(MonoObject *obj)
            : object(obj) {

            Init();
        };

        bool Init()
        {
            mono_runtime_object_init(object);

            klass = mono_object_get_class(object);

            void *iter = nullptr;

            MonoClassField *field = nullptr;

            while ((field = mono_class_get_fields(klass, &iter)))
            {

                auto inst = std::make_shared<mono::Field>(field);

                MonoObject *fieldValueObj = nullptr;
                mono_field_get_value(object, field, &fieldValueObj);

                if (fieldValueObj)
                {
                    inst->SetValue(fieldValueObj);
                }
                else
                {
                    ztclog::warn("field value obj is null");
                }
                m_Fields[inst->m_FieldName] = inst;

            }
            return true;
        };
        bool HasField(const std::string& name)
        {
            auto it = m_Fields.find(name);
            return it != m_Fields.end();
        };

        bool SetField(const std::string &name, MonoObject *object)
        {
            bool res = HasField(name);
            
            if (res)
            {
                auto it = m_Fields.find(name);
                it->second->SetValue(object);
            }
            return res;
        };

        template <typename T>
        bool SetField(const std::string &name, const T &t, MonoClass& klass) {
            mono::Converter converter;
            MonoObject *object;
            if (!converter.Encode(object, t, klass))
            {
                return false;
            }
            return SetField(name, object);
        };

        MonoObject *Invoke(mono::Method *method, mono::Args args)
        {
            MonoObject *result = nullptr;
            return method->Invoke(args, this->object);
        }

        template <typename T>
        bool Invoke2(mono::Method *method, mono::Args args, T &t)
        {
            auto result = Invoke(method, args);
            mono::Converter converter;

            bool res = converter.Decode(result, t);

            return true;
        };
    };
};